home *** CD-ROM | disk | FTP | other *** search
- /**********************************************************
- Copyright (c) 1990 Richard Lesh, All Rights Reserved
- **********************************************************/
-
- #include <MacHeaders>
- #include <Color.h>
- #include <VRetraceMgr.h>
-
- /**********************************************************
- Module: Cursor Control
-
- This module provides support for the use of dynamic cursors
- which give better feedback to the user that the program is
- working properly. This module has an interface similar to
- the MPW version CursorCtrl.c but does not necessarily
- function in an identical manner. (MPW does not support
- color cursors). This version uses a VBL task to smooth out
- the cursor animation. It does not, hoever, allow the cursor
- to continue spinning in the event of a system crash. The
- application must execute SpinCursor on a regular basis to
- keep the cursor spinning. RotateCursor is not supported in
- this version. InitCursorCtrl is called to initialize the
- cursors or to change them to a new set of cursors.
- StopCursor is called to halt the cursor animation so that
- it can then be set to the arrow or other application defined
- cursors.
-
- Routines:
- InitCursorCtrl() -- Initializes the module & loads cursors.
- QuitCursorCtrl() -- Cleans up when animated cursors are no
- longer needed.
- SpinCursor() -- Starts or continues cursor animation.
- StopCursor() -- Stops the cursor animation.
- SetCursorSpeed() -- Sets the speed of cursor rotation.
- **********************************************************/
-
- /**********************************************************
- Definitions
- **********************************************************/
- #ifndef NULL
- #define NULL 0L
- #endif
-
- /**********************************************************
- TypeDefs and Enums
- **********************************************************/
- /* Animated cursor control structure 'acur' */
- typedef struct {
- short n;
- short index;
- union {
- Handle cursorHdl;
- short resID;
- }frame[1];
- }acurRec,*acurPtr,**acurHandle;
-
- /**********************************************************
- Private Globals
- **********************************************************/
- /* Current 'acur' resource handle */
- static acurHandle gCurrentHdl=NULL;
- /* True if using color cursors */
- static Boolean gColorCursor=FALSE;
- /* VBL task record for the cursor */
- static VBLTask *gCursorTask=NULL;
- /* Number of cycles to spin cursor */
- static short gSpinCycles=0;
- /* Number of ticks between changes */
- static short gSpeed=10;
- /* TRUE after SpinCursor() and FALSE after StopCursor() */
- static Boolean isCursorRunning=FALSE;
-
- /**********************************************************
- Prototypes
- **********************************************************/
- /* Functions to be exported */
- void InitCursorCtrl(acurHandle h);
- void QuitCursorCtrl(void);
- void SpinCursor(short cycles);
- void StopCursor(void);
- void SetCursorSpeed(short newSpeed);
-
- /* Functions to be used internally */
- void DisposCursors(void);
- Boolean GetMonoCursors(acurHandle h);
- Boolean GetColorCursors(acurHandle h);
- pascal void SpinCursorTask(void);
-
- Boolean hasColorQD(void);
- VBLTask *InstallVBLTask(ProcPtr proc,short ticks);
- void RemoveVBLTask(VBLTask *taskPtr);
-
- /**********************************************************
- Routine: InitCursorCtrl(acurHandle resHdl)
-
- This routine initializes the CursorCtrl module using the
- 'acur' resource indicated via the argument resHdl. If
- resHdl is NULL then the 'acur' resource with ID=128 is
- loaded. If the machine has ColorQD, InitCursorCtrl first
- attempts to load the color cursor 'crsr' resources with the
- IDs indicated in the 'acur' resource. If this fails or if
- the machine does not have ColorQD, InitCursorCtrl attempts
- to load the normal cursor 'CURS' resources with the IDs
- indicated in the 'acur' resource. If this action fails,
- all subsequent calls to SpinCursor will simply set the
- cursor to the watch cursor.
-
- InitCursorCtrl should be called as follows:
-
- InitCursorCtrl((acurHandle)GetResource('acur',200));
-
- If GetResource is unable to find the 'acur' resource it
- returns NULL which can be handled by InitCursorCtrl.
- InitCursorCtrl will mark the resource as unpurgable and
- will be responsible for releasing the all cursor storage
- and the 'acur' resource when called again with a new 'acur'
- handle. If the new handle is the same as the current
- handle, nothing will be done. Since 'crsr' resources are
- just templates for a color cursor, you should make sure
- that all 'crsr' resources are marked purgable in the
- resource file since GetCCursor does not release these
- resources.
- **********************************************************/
-
- void InitCursorCtrl(acurHandle h)
- {
- short i,j;
- Boolean useColorCursors;
-
- useColorCursors=hasColorQD();
-
- if (!h) h=(void *)GetResource('acur', 128);
- if (h && h!=gCurrentHdl){
- HNoPurge(h);
- MoveHHi(h);
- HLock(h);
-
- /**********************************************************
- Get new cursors.
- **********************************************************/
- StopCursor();
- if (useColorCursors)
- useColorCursors=GetColorCursors(h);
- if (!useColorCursors && !GetMonoCursors(h)) return;
-
- DisposCursors();
- gCurrentHdl=h;
- gColorCursor=useColorCursors;
- (*h)->index=0;
- }
- }
-
- /**********************************************************
- Routine: QuitCursorCtrl()
-
- Shuts down the cursor control module.
- **********************************************************/
-
- void QuitCursorCtrl()
- {
- DisposCursors();
- if (gCursorTask) RemoveVBLTask(gCursorTask);
- gCursorTask=NULL;
- }
-
- /**********************************************************
- Routine: DisposCursors()
-
- Disposes the cursors pointed to in the 'acur' structure.
- **********************************************************/
-
- void DisposCursors()
- {
- register short i,j;
-
- StopCursor();
- if (gCurrentHdl){
- j=(*gCurrentHdl)->n;
- if (gColorCursor)
- for (i=0;i<j;i++)
- DisposCCursor((*gCurrentHdl)->frame[i].cursorHdl);
- else
- for (i=0;i<j;i++)
- DisposHandle((*gCurrentHdl)->frame[i].cursorHdl);
- ReleaseResource(gCurrentHdl);
- gCurrentHdl=NULL;
- }
- }
-
- /**********************************************************
- Routine: GetMonoCursors(acurHandle h)
-
- This is an internal routine that loads the normal cursors
- ('CURS') from the resource file. In the 'acur' resource,
- the resource ID of the cursor is stored in the frame union.
- When the cursor has been loaded, its handle is then stored
- in the frame union. The function returns TRUE if it was
- successful at loading in all the cursors and FALSE if there
- was an error.
- **********************************************************/
-
- static Boolean GetMonoCursors(acurHandle h)
- {
- short i,j;
- CursHandle cursHdl;
-
- if (h){
- j=(*h)->n;
- for (i=0;i<j;i++){
- cursHdl=GetCursor((*h)->frame[i].resID);
- if (cursHdl==NULL){
- for (j=0;j<i;j++)
- DisposHandle((*h)->frame[j].cursorHdl);
- return(FALSE);
- }else{
- DetachResource(cursHdl);
- (*h)->frame[i].cursorHdl=(Handle)cursHdl;
- }
- }
- }
- return(TRUE);
- }
-
- /**********************************************************
- Routine: GetColorCursors(acurHandle h)
-
- This is an internal routine that loads the color cursors
- ('crsr') from the resource file. In the 'acur' resource,
- the resource ID of the cursor is stored in the frame union.
- When the cursor has been loaded, its handle is then stored
- in the frame union. The function returns TRUE if it was
- successful at loading in all the cursors and FALSE if there
- was an error. The 'crsr' resources should be set purgable.
- **********************************************************/
-
- static Boolean GetColorCursors(acurHandle h)
- {
- short i,j;
- CCrsrHandle cursHdl;
- Boolean result=TRUE;
-
- if (h){
- j=(*h)->n;
- HideCursor();
- for (i=0;i<j;i++){
- cursHdl=GetCCursor((*h)->frame[i].resID);
- if (cursHdl==NULL){
- for (j=0;j<i;j++)
- DisposCCursor((*h)->frame[j].cursorHdl);
- result=FALSE;
- break;
- }else{
- (*h)->frame[i].cursorHdl=(Handle)cursHdl;
- SetCCursor((*h)->frame[i].cursorHdl);
- }
- }
- InitCursor();
- }
- return(result);
- }
-
- /**********************************************************
- Routine: SpinCursor(short seconds)
-
- This routine sets gSpinCycles to seconds*60/gSpeed which is
- the number of times that the cursor should spin before
- stopping. The seconds parameter should therefore be set to
- just slightly longer than the time estimated to execute the
- code up to the next SpinCursor call. When the gSpinCycles
- value counts down to zero the cursor will no longer spin
- and the user will thereby be notified that something is
- awry. If there is no current 'acur' resource, this routine
- will set the cursor the the watch cursor.
- **********************************************************/
-
- void SpinCursor(short seconds)
- {
- static long counter=0;
-
- if (gCurrentHdl==0) InitCursorCtrl(NULL);
- if (gCurrentHdl){
- if (!gCursorTask)
- gCursorTask=InstallVBLTask((ProcPtr)SpinCursorTask,
- gSpeed);
- if (gCursorTask){
- if (gSpinCycles==0){
- if (gColorCursor)
- SetCCursor((*gCurrentHdl)->frame[
- (*gCurrentHdl)->index].cursorHdl);
- else
- SetCursor(*(*gCurrentHdl)->frame[
- (*gCurrentHdl)->index].cursorHdl);
- }
- gSpinCycles=seconds*60/gSpeed;
- }
- }else
- SetCursor(*GetCursor(watchCursor));
- isCursorRunning=TRUE;
- }
-
- /**********************************************************
- Routine: SpinCursorTask()
-
- This is the VBL task routine. If the gSpinCycles global is
- not zero it will decrement gSpinCycles and then advance the
- cursor to the next one specified by the 'acur' resource in
- gCurrentHdl. This routine does call SetCCursor which can
- call the memory manager. The results could be severe if
- the cursors had not be preloaded. Since application VBL
- tasks are only called when the application is in the
- foreground, the global CurrentA5 used by SetCurrentA5()
- will be the correct value.
- **********************************************************/
-
- static pascal void SpinCursorTask()
- {
- long oldA5;
-
- oldA5=SetCurrentA5();
- gCursorTask->vblCount=gSpeed;
- if (gSpinCycles){
- gSpinCycles--;
- (*gCurrentHdl)->index++;
- (*gCurrentHdl)->index%=(*gCurrentHdl)->n;
- if (gColorCursor)
- SetCCursor((*gCurrentHdl)->frame[(*gCurrentHdl)
- ->index].cursorHdl);
- else
- SetCursor(*(*gCurrentHdl)->frame[(*gCurrentHdl)
- ->index].cursorHdl);
- }
- SetA5(oldA5);
- }
-
- /**********************************************************
- Routine: StopCursor()
-
- This routine will stop the cursor animation by setting the
- gSpinCycles global to zero. This must be called before the
- application changes the cursor to a normal cursor because
- the VBL task will continue to set the cursor to the
- animated cursors until the gSpinCycles count has run out.
- **********************************************************/
-
- void StopCursor()
- {
- gSpinCycles=0;
- isCursorRunning=FALSE;
- if (gCursorTask) RemoveVBLTask(gCursorTask);
- gCursorTask=NULL;
- }
-
- /**********************************************************
- Routine: SetCursorSpeed(short newSpeed)
-
- This routine sets the number of ticks that must occur
- before the cursor will change to the next one in the
- animation sequence.
- **********************************************************/
-
- void SetCursorSpeed(short newSpeed)
- {
- if (newSpeed>0 && newSpeed<=60)
- gSpeed=newSpeed;
- }
-
- /**********************************************************
- Routine: Boolean hasColorQD()
-
- Predicate that returns TRUE if the current machine supports
- Color Quickdraw.
- **********************************************************/
-
- #define SysEnvironsVersion 2
- Boolean hasColorQD()
- {
- OSErr theErr;
- SysEnvRec theWorld;
-
- theErr=SysEnvirons(SysEnvironsVersion,&theWorld);
-
- if (theErr == 0 && theWorld.hasColorQD) return(TRUE);
- else return(FALSE);
- }
-
- /**********************************************************
- Routine: VBLTask *InstallVBLTask(ProcPtr proc,short ticks)
-
- This routine installs the VLBTask pointed to by the ProcPtr
- which will begin execution after the number of ticks
- specified. A pointer to a VBL task record is returned.
- NULL is returned if the task could not be set up.
- **********************************************************/
-
- VBLTask *InstallVBLTask(ProcPtr proc,short ticks)
- {
- OSErr err;
- VBLTask *taskPtr;
-
- taskPtr=(VBLTask *)NewPtr(sizeof(VBLTask));
- if (taskPtr){
- taskPtr->qType=vType;
- taskPtr->vblAddr=proc;
- taskPtr->vblCount=ticks;
- taskPtr->vblPhase=0;
- err=VInstall((QElemPtr)taskPtr);
- if (err!=noErr){
- DisposPtr(taskPtr);
- taskPtr=NULL;
- }
- }
- return(taskPtr);
- }
-
- /**********************************************************
- Routine: void RemoveVBLTask(VBLTask *taskPtr)
-
- Removes the VBL task specified by taskPtr that was
- installed using InstallVBLTask. It also disposes of the
- memory set up in that call.
- **********************************************************/
-
- void RemoveVBLTask(VBLTask *taskPtr)
- {
- VRemove((QElemPtr)taskPtr);
- DisposPtr(taskPtr);
- }
-